/*
 * Copyright (c)  2015~2020, xforceplus
 * All rights reserved.
 * Project:datacare-orm
 * Id: NumberKit.java   2020-09-29 22-15-14
 * Author: Evan
 */
package cn.datacare.orm.util.kit;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * <p>
 * Title:
 * </p>
 * <p>
 * Description:
 * </p>
 * <p>
 * Copyright: 2015~2020
 * </p>
 * <p>
 * Company/Department: xforceplus
 * </p>
 *
 * @author Evan
 * <b>Creation Time:</b> 2020-09-29 22-15-14
 * @since V1.0
 */
public class NumberKit {
    /**
     * 将Number转化为期望类型
     *
     * @param number
     * @param targetClass
     * @return
     */
    public static Number convertNumberToTargetClass(Number number, Class targetClass) {
        NumberKit.checkParams(number, targetClass);
        if (targetClass.isInstance(number)) {
            return number;
        } else if (Byte.class == targetClass || byte.class == targetClass) {
            long value = number.longValue();
            if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
                throw new IllegalArgumentException(number.getClass().getName() + "无法转化为目标对象" + targetClass.getName());
            }
            return new Byte(number.byteValue());
        } else if (Short.class == targetClass || short.class == targetClass) {
            long value = number.longValue();
            if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
                throw new IllegalArgumentException(number.getClass().getName() + "无法转化为目标对象" + targetClass.getName());
            }
            return new Short(number.shortValue());
        } else if (Integer.class == targetClass || int.class == targetClass) {
            long value = number.longValue();
            if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
                throw new IllegalArgumentException(number.getClass().getName() + "无法转化为目标对象" + targetClass.getName());
            }
            return new Integer(number.intValue());
        } else if (Long.class == targetClass || long.class == targetClass) {
            BigInteger bigInt = null;
            if (number instanceof BigInteger) {
                bigInt = (BigInteger) number;
            } else if (number instanceof BigDecimal) {
                bigInt = ((BigDecimal) number).toBigInteger();
            }
            // Effectively analogous to JDK 8's BigInteger.longValueExact()
            if (bigInt != null && (bigInt.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0 || bigInt.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0)) {
                throw new IllegalArgumentException(number.getClass().getName() + "无法转化为目标对象" + targetClass.getName());
            }
            return new Long(number.longValue());
        } else if (BigInteger.class == targetClass) {
            if (number instanceof BigDecimal) {
                // do not lose precision - use BigDecimal's own conversion
                return ((BigDecimal) number).toBigInteger();
            } else {
                // original value is not a Big* number - use standard long conversion
                return BigInteger.valueOf(number.longValue());
            }
        } else if (Float.class == targetClass || float.class == targetClass) {
            return new Float(number.floatValue());
        } else if (Double.class == targetClass || double.class == targetClass) {
            return new Double(number.doubleValue());
        } else if (BigDecimal.class == targetClass) {
            // always use BigDecimal(String) here to avoid unpredictability of BigDecimal(double)
            // (see BigDecimal javadoc for details)
            return new BigDecimal(number.toString());
        } else {
            throw new IllegalArgumentException(number.getClass().getName() + "无法转化为目标对象" + targetClass.getName());
        }
    }


    private static <T> Boolean checkParams(Object str, Class<T> targetClass) {
        if (str == null) {
            throw new IllegalArgumentException("转化值不能为空");
        }
        if (targetClass == null) {
            throw new IllegalArgumentException("期望类型不能为空");
        }
        return Boolean.TRUE;
    }


    /**
     * 将String转化为期望类型
     *
     * @param text
     * @param targetClass
     * @return
     */
    public static <T extends Number> T parseNumber(String text, Class<T> targetClass) {
        //检查参数是否为有效的
        NumberKit.checkParams(text, targetClass);
        String trimmed = StringKit.trimAllWhitespace(text);

        if (targetClass.equals(Byte.class)) {
            return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed));
        } else if (targetClass.equals(Short.class)) {
            return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed));
        } else if (targetClass.equals(Integer.class)) {
            return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed));
        } else if (targetClass.equals(Long.class)) {
            return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed));
        } else if (targetClass.equals(BigInteger.class)) {
            return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed));
        } else if (targetClass.equals(Float.class)) {
            return (T) Float.valueOf(trimmed);
        } else if (targetClass.equals(Double.class)) {
            return (T) Double.valueOf(trimmed);
        } else if (targetClass.equals(BigDecimal.class) || targetClass.equals(Number.class)) {
            return (T) new BigDecimal(trimmed);
        } else {
            throw new IllegalArgumentException("无法将[" + text + "]转化为期望类型:[" + targetClass.getName() + "]");
        }
    }


    /**
     * Signopt 0x HexDigits
     * Signopt 0X HexDigits
     * Signopt # HexDigits
     * 是否为十六进制
     *
     * @param value
     * @return
     */
    private static boolean isHexNumber(String value) {
        int index = (value.startsWith("-") ? 1 : 0);
        return (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index));
    }


    /**
     * 解码BigInteger
     *
     * @param value
     * @return
     */
    private static BigInteger decodeBigInteger(String value) {
        //进制和脚标
        int radix = 10, index = 0;
        //负标记
        boolean negative = false;

        // 处理减
        if (value.startsWith("-")) {
            negative = true;
            index++;
        }
        if (value.startsWith("0x", index) || value.startsWith("0X", index)) {
            // 处理进制：16进制，脚标移动2
            index += 2;
            radix = 16;
        }

        else if (value.startsWith("#", index)) {
            // 处理进制：16进制，脚标移动1
            index++;
            radix = 16;
        }

        else if (value.startsWith("0", index) && value.length() > 1 + index) {
            // 处理进制：8进制，脚标移动1
            index++;
            radix = 8;
        }

        //返回BigInteger
        BigInteger result = new BigInteger(value.substring(index), radix);
        return (negative ? result.negate() : result);
    }
}
